"use strict";

const _ = require("lodash");

/**
 * Recursively converts a swagger type description into a typescript type, i.e., a model for our mustache
 * template.
 *
 * Not all type are currently supported, but they should be straightforward to add.
 *
 * @param swaggerType a swagger type definition, i.e., the right hand side of a swagger type definition.
 * @returns a recursive structure representing the type, which can be used as a template model.
 */
function convertType(swaggerType, swagger) {
  var typespec = { description: swaggerType.description, isEnum: false };

  // eslint-disable-next-line no-prototype-builtins
  if (swaggerType.hasOwnProperty("schema")) {
    return convertType(swaggerType.schema);
  }

  if (_.isString(swaggerType.$ref)) {
    typespec.tsType = "ref";
    typespec.target = swaggerType.$ref.substring(
      swaggerType.$ref.lastIndexOf("/") + 1
    );
    // eslint-disable-next-line no-prototype-builtins
  } else if (swaggerType.hasOwnProperty("enum")) {
    typespec.tsType = swaggerType.enum
      .map(function(str) {
        return JSON.stringify(str);
      })
      .join(" | ");
    typespec.isAtomic = true;
    typespec.isEnum = true;
  } else if (swaggerType.type === "string") {
    typespec.tsType = "string";
  } else if (swaggerType.type === "number" || swaggerType.type === "integer") {
    typespec.tsType = "number";
  } else if (swaggerType.type === "boolean") {
    typespec.tsType = "boolean";
  } else if (swaggerType.type === "array") {
    typespec.tsType = "array";
    typespec.elementType = convertType(swaggerType.items);
  } else {
    /* If (swaggerType.type === 'object') */
    // Remaining types are created as objects
    // eslint-disable-next-line no-lonely-if
    if (
      swaggerType.minItems >= 0 &&
      // eslint-disable-next-line no-prototype-builtins
      swaggerType.hasOwnProperty("title") &&
      !swaggerType.$ref
    ) {
      typespec.tsType = "any";
    } else {
      typespec.tsType = "object";
      typespec.properties = [];
      if (swaggerType.allOf) {
        _.forEach(swaggerType.allOf, function(ref) {
          if (ref.$ref) {
            var refSegments = ref.$ref.split("/");
            var name = refSegments[refSegments.length - 1];
            _.forEach(swagger.definitions, function(
              definition,
              definitionName
            ) {
              if (definitionName === name) {
                var property = convertType(definition, swagger);
                Array.prototype.push.apply(
                  typespec.properties,
                  property.properties
                );
              }
            });
          } else {
            var property = convertType(ref);
            Array.prototype.push.apply(
              typespec.properties,
              property.properties
            );
          }
        });
      }

      _.forEach(swaggerType.properties, function(propertyType, propertyName) {
        var property = convertType(propertyType);
        property.name = propertyName;

        property.optional = true;
        if (
          swaggerType.required &&
          swaggerType.required.indexOf(propertyName) !== -1
        ) {
          property.optional = false;
        }

        typespec.properties.push(property);
      });
    }
  } /* Else {
     // type unknown or unsupported... just map to 'any'...
     typespec.tsType = 'any';
     } */

  // Since Mustache does not provide equality checks, we need to do the case distinction via explicit booleans
  typespec.isRef = typespec.tsType === "ref";
  typespec.isObject = typespec.tsType === "object";
  typespec.isArray = typespec.tsType === "array";
  typespec.isAtomic =
    typespec.isAtomic ||
    _.includes(["string", "number", "boolean", "any"], typespec.tsType);

  return typespec;
}

const format = (date = new Date(), fmt = "yyyy-MM-dd HH:mm:ss") => {
  const o = {
    "M+": date.getMonth() + 1, // 月份
    "d+": date.getDate(), // 日
    "h+": date.getHours(), // 小时
    "H+": date.getHours(), // 小时
    "m+": date.getMinutes(), // 分
    "s+": date.getSeconds(), // 秒
    "q+": Math.floor((date.getMonth() + 3) / 3), // 季度
    S: date.getMilliseconds() // 毫秒
  };
  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(
      RegExp.$1,
      String(date.getFullYear()).substr(4 - RegExp.$1.length)
    );
  }

  for (var k in o) {
    if (new RegExp("(" + k + ")").test(fmt)) {
      fmt = fmt.replace(
        RegExp.$1,
        RegExp.$1.length === 1
          ? o[k]
          : ("00" + o[k]).substr(String(o[k]).length)
      );
    }
  }

  return fmt;
};

module.exports.convertType = convertType;
module.exports.format = format;
